package com.hao.order.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hao.common.code.ErrorCode;
import com.hao.common.execption.BusinessException;
import com.hao.order.app.feign.ProductFeign;
import com.hao.order.app.feign.UserFeign;
import com.hao.order.app.mapper.OrderMapper;
import com.hao.order.app.model.dto.*;
import com.hao.order.app.model.entity.Order;
import com.hao.order.app.model.vo.OrderVO;
import com.hao.order.app.service.OrderService;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.hao.order.app.constant.RedisConstant.REDIS_LOCK;

/**
* @author haoge
* @description 针对表【order(订单表)】的数据库操作Service实现
* @createDate 2023-05-23 18:47:57
*/
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order>
    implements OrderService{

    @Resource
    private UserFeign userFeign;

    @Resource
    private ProductFeign productFeign;

    @Resource
    private RedissonClient redissonClient;

    @Override
    //@GlobalTransactional
    public boolean createOrder(CreateOrderRequest createOrderRequest) {
        Double price = createOrderRequest.getPrice();
        Integer productCount = createOrderRequest.getProductCount();
        Long productId = createOrderRequest.getProductId();
        Long userId = createOrderRequest.getUserId();

        RLock lock = redissonClient.getLock(REDIS_LOCK + ":create:order:"+productId);
        try {
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                    System.out.println("lock:" + Thread.currentThread().getName());
                    //扣库存
                    boolean result = productFeign.decreaseProductStock(productId, productCount);
                    if (!result) {
                        throw new BusinessException(ErrorCode.OPERATION_ERROR);
                    }
                    Order order = new Order();
                    BeanUtils.copyProperties(order,createOrderRequest);
                    String address = userFeign.getUserAddress(userId);
                    order.setStatus(1L);
                    order.setTotalNum(price * productCount);
                    order.setAddress(address);
                    // TODO: 2023/5/25 如果订单已经存在则修改订单
                    boolean save = this.save(order);
                    if (!save) {
                        throw new BusinessException(ErrorCode.OPERATION_ERROR);
                    }
                    byte[] bytes = String.valueOf(order.getId()).getBytes();
                    // TODO: 2023/5/24
                    //SendMessageToMq.sendDelayMsg("order",bytes);
                    return save;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }finally {
            if (lock.isHeldByCurrentThread()) {
                System.out.println("unLock:" + Thread.currentThread().getName());
                lock.unlock();
            }
        }
    }

    @Override
    public double getCurrentAmount(String orderIds) {

        String[] split = orderIds.split(",");
        List<Long> orderIdList = Arrays.stream(split).map(Long::valueOf).collect(Collectors.toList());
        QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
        orderQueryWrapper.select("totalNum");
        orderQueryWrapper.eq("status", 1);
        orderQueryWrapper.in("id", orderIdList);

        List<Order> orderList = this.list(orderQueryWrapper);
        double sum = orderList.stream().mapToDouble(Order::getTotalNum).sum();
        if (sum < 0) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        return sum;
    }

    @Override
    public boolean pay(PayOrderRequest payOrderRequest) {

        Long userId = payOrderRequest.getUserId();
        Double amount = payOrderRequest.getAmount();
        List<Long> orderIdList = Arrays.stream(payOrderRequest.getOrderIds().split(",")).map(Long::valueOf).collect(Collectors.toList());

        RLock lock = redissonClient.getLock(REDIS_LOCK + ":pay:" + userId);

        try {
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                    boolean b = userFeign.decreaseBalance(userId, amount);
                    if (!b) {
                        throw new BusinessException(ErrorCode.OPERATION_ERROR,"余额不足");
                    }
                    UpdateWrapper<Order> orderUpdateWrapper = new UpdateWrapper<>();
                    orderUpdateWrapper.in("id", orderIdList);
                    orderUpdateWrapper.set("status", 2L);
                    boolean update = this.update(orderUpdateWrapper);
                    if (!update) {
                        throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                    }
                    return true;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    public Page<OrderVO> getOrderByUser(QueryUserOrderRequest queryUserOrderRequest) {

        Long userId = queryUserOrderRequest.getUserId();
        Integer status = queryUserOrderRequest.getStatus();
        int pageNum = queryUserOrderRequest.getPageNum();
        int pageSize = queryUserOrderRequest.getPageSize();

        QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
        orderQueryWrapper.eq("userId", userId);
        orderQueryWrapper.eq("status", status);

        Page<Order> page = this.page(new Page<>(pageNum, pageSize), orderQueryWrapper);
        Page<OrderVO> orderVOPage = new PageDTO<>(page.getCurrent(), page.getSize(), page.getTotal());

        List<OrderVO> orderVOList = page.getRecords().stream().map(m -> {
            OrderVO orderVO = new OrderVO();
            try {
                BeanUtils.copyProperties(orderVO, m);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
            return orderVO;
        }).collect(Collectors.toList());
        orderVOPage.setRecords(orderVOList);
        return orderVOPage;
    }

    @Override
    public boolean deleteOrder(Long id, Integer count) {
        QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
        orderQueryWrapper.eq("id", id);
        Order order = this.getById(id);
        if (order.getProductCount() <= count) {
            return this.remove(orderQueryWrapper);
        } else {
            order.setProductCount(order.getProductCount() - count);
            return this.updateById(order);
        }
    }

    @Override
    public boolean updateOrderById(UpdateOrderRequest updateOrderRequest) {
        String address = updateOrderRequest.getAddress();
        Long id = updateOrderRequest.getId();
        Long userId = updateOrderRequest.getUserId();
        Date payTime = updateOrderRequest.getPayTime();
        Long status = updateOrderRequest.getStatus();
        Integer productCount = updateOrderRequest.getProductCount();

        UpdateWrapper<Order> orderUpdateWrapper = new UpdateWrapper<>();
        orderUpdateWrapper.eq("id", id);
        orderUpdateWrapper.eq("userId", userId);

        if (status != null && status > 0) {
            orderUpdateWrapper.set("status", status);
        }
        if (productCount != null && productCount > 0) {
            orderUpdateWrapper.set("productCount", productCount);
        }
        if (StringUtils.isNotBlank(address)) {
            orderUpdateWrapper.set("address", address);
        }
        if (payTime != null) {
            orderUpdateWrapper.set("payTime", payTime);
        }
        return this.update(orderUpdateWrapper);
    }

    @Override
    public Page<Order> listOrderByPage(ListOrderRequest listOrderRequest) throws ParseException {
        Long id = listOrderRequest.getId();
        Long userId = listOrderRequest.getUserId();
        Long productId = listOrderRequest.getProductId();
        Double price = listOrderRequest.getPrice();
        Integer productCount = listOrderRequest.getProductCount();
        Double totalNum = listOrderRequest.getTotalNum();
        Long status = listOrderRequest.getStatus();
        String address = listOrderRequest.getAddress();
        String payTime = listOrderRequest.getPayTime();
        String createTime = listOrderRequest.getCreateTime();
        String updateTime = listOrderRequest.getUpdateTime();
        int pageSize = listOrderRequest.getPageSize();
        int pageNum = listOrderRequest.getPageNum();

        QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
        if (id != null && id > 0) {
            orderQueryWrapper.eq("id", id);
        }
        if (userId != null && userId > 0) {
            orderQueryWrapper.eq("userId", userId);
        }
        if (productId != null && productId > 0) {
            orderQueryWrapper.eq("productId", productId);
        }
        if (price != null && price >= 0) {
            orderQueryWrapper.eq("price", price);
        }
        if (productCount != null && productCount > 0) {
            orderQueryWrapper.eq("productCount", productCount);
        }
        if (totalNum != null && totalNum >= 0) {
            orderQueryWrapper.eq("totalNum", totalNum);
        }
        if (status != null && status > 0) {
            orderQueryWrapper.eq("status", status);
        }
        if (StringUtils.isNotBlank(address)) {
            orderQueryWrapper.eq("address", address);
        }
        SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if (createTime != null) {
            Date parse = ft.parse(createTime);
            orderQueryWrapper.eq("createTime", parse);
        }
        if (updateTime != null) {
            Date parse = ft.parse(updateTime);
            orderQueryWrapper.eq("updateTime", parse);
        }
        if (payTime != null) {
            Date parse = ft.parse(payTime);
            orderQueryWrapper.eq("payTime", parse);
        }
        Page<Order> page = new Page<>(pageNum, pageSize);

        return this.page(page, orderQueryWrapper);
    }
}




